﻿
namespace Patterns4Net.ArchitectureExplorer.ViewModels.Results
{
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Threading;
    using Caliburn.Micro;
    using Patterns4Net.Attributes.DesignPatterns;

    /// <summary>
    /// Helper class that contains factory methods for actions that
    /// hides or shows the progress bar in the GUI.
    /// </summary>
    public class Progress
    {
        /// <summary>
        /// Creates a result that shows the progress bar.
        /// </summary>
        public static IResult Show()
        {
            return new ProgressShow();
        }

        /// <summary>
        /// Creates a result that hides the progress bar.
        /// </summary>
        public static IResult Hide()
        {
            return new ProgressHide(); 
        }

        /// <summary>
        /// Hides the progress bar.
        /// </summary>
        private class ProgressHide : ProgressBase
        {
            protected override void Execute(ActionExecutionContext context, ProgressBar progressBar)
            {
                progressBar.Visibility = Visibility.Hidden; 
            }            
        }

        /// <summary>
        /// Shows the progress bar.
        /// </summary>
        private class ProgressShow : ProgressBase
        {
            protected override void Execute(ActionExecutionContext context, ProgressBar progressBar)
            {
                progressBar.Visibility = Visibility.Visible;
            }
        }

        /// <summary>
        /// Abstract class for manipulation with the progress bar. 
        /// It is capable of location of the progress bar instance, which is done using a result execution context, 
        /// and it also provides means to access the progress bar instance in thread safe manner.
        /// </summary>
        private abstract class ProgressBase : IResult
        {
            /// <summary>
            /// Executes the result using the specified context.
            /// </summary>
            /// <param name="context">The context.</param>
            public void Execute(ActionExecutionContext context)
            {
                var control = context.View as FrameworkElement;
                if (control == null)
                {
                    throw new ArgumentException("context.View is not FrameworkElement.");
                }

                control.Dispatcher.BeginInvoke(new UpdateProgressBar(() => this.Execute(context, control)), DispatcherPriority.Normal);
            }

            /// <summary>
            /// Walks the WPF visual tree until it finds the progress bar, when the progress bar is found 
            /// the <see cref="Execute(ActionExecutionContext, ProgressBar)"/> method is executed.
            /// </summary>
            [TemplateMethod]
            private void Execute(ActionExecutionContext context, FrameworkElement control)
            {
                ProgressBar progressBar = null;
                var stack = new Stack<FrameworkElement>(new[] { control });
                while (stack.Count > 0 && progressBar == null)
                {
                    control = stack.Pop();
                    if (control is ProgressBar)
                    {
                        progressBar = (ProgressBar)control;
                        this.Execute(context, progressBar);
                    }

                    int childrenCount = VisualTreeHelper.GetChildrenCount(control);
                    for (int i = 0; i < childrenCount; i++)
                    {
                        var child = VisualTreeHelper.GetChild(control, i) as FrameworkElement;
                        if (child != null)
                        {
                            stack.Push(child);
                        }
                    }
                }

                if (this.Completed != null)
                {
                    this.Completed(this, new ResultCompletionEventArgs { WasCancelled = false });
                }                
            }

            /// <summary>
            /// Occurs when execution has completed.
            /// </summary>
            public event EventHandler<ResultCompletionEventArgs> Completed;

            /// <summary>
            /// Dummy delegate used as an argument for BeginInvoke method of the Dispatcher object.
            /// </summary>
            private delegate void UpdateProgressBar();

            /// <summary>
            /// This method should perform desired action on the progress bar.
            /// This method is executed in the GUI dispatcher thread.
            /// </summary>
            /// <param name="context">The result execution context.</param>
            /// <param name="progressBar">The progress bar instance.</param>
            protected abstract void Execute(ActionExecutionContext context, ProgressBar progressBar);
        }
    }
}
